﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using vJine.Core.IoC;

namespace vJine.Core.IO.Json {
    /// <summary>
    /// Json序列化、反序列化缓存类
    /// </summary>
    public class JsonHelperCache {
        static JsonHelperCache() {
            JsonHelperCache.Init_M();
        }

        private JsonHelperCache() {
        }

        static Dictionary<Type, Exec<object, StreamWriter>> ToStringCache = new Dictionary<Type, Exec<object, StreamWriter>>();
        static Type TjsonHelper = typeof(JsonHelper<>);
        /// <summary>
        /// 缓存指定实体类型的序列化代理
        /// </summary>
        /// <param name="Tentity">实体类型</param>
        /// <returns>序列化代理</returns>
        public static Exec<object, StreamWriter> ToString(Type Tentity) {
            lock(JsonHelperCache.ToStringCache) {
                if(JsonHelperCache.ToStringCache.ContainsKey(Tentity)) {
                    return JsonHelperCache.ToStringCache[Tentity];
                }

                MethodInfo obj_toString = 
                    TjsonHelper.MakeGenericType(Tentity).GetMethod("ToString", new Type[] { Tentity, typeof(StreamWriter) });

                DynamicMethod dm =
                    new DynamicMethod("", Reflect.@void, new Type[] { Reflect.@object, typeof(StreamWriter) }, typeof(JsonHelperCache));
                ILGenerator ilGen = dm.GetILGenerator();

                ilGen.Emit(OpCodes.Ldarg_0);
                ilGen.Emit(OpCodes.Unbox_Any, Tentity);
                ilGen.Emit(OpCodes.Ldarg_1);
                Emit.Opc_Call(ilGen, obj_toString);
                ilGen.Emit(OpCodes.Ret);

                Exec<object, StreamWriter> helper =
                    dm.CreateDelegate(typeof(Exec<object, StreamWriter>)) as Exec<object, StreamWriter>;
                JsonHelperCache.ToStringCache.Add(Tentity, helper);

                return helper;
            }
        }

        static Dictionary<Type, Call<object, Stream>> ParseCache = new Dictionary<Type, Call<object, Stream>>();
        /// <summary>
        /// 缓存指定实体类型的反序列化代理
        /// </summary>
        /// <param name="Tjson">实体类型</param>
        /// <returns>反序列化代理</returns>
        public static Call<object, Stream> Parse(Type Tjson) {
            lock (ParseCache) {
                if (ParseCache.ContainsKey(Tjson)) {
                    return ParseCache[Tjson];
                }

                MethodInfo obj_parse =
                    TjsonHelper.MakeGenericType(Tjson).GetMethod("Parse", new Type[] { Reflect.@Stream });

                DynamicMethod dm = new DynamicMethod("", Reflect.@object, new Type[] { Reflect.@Stream });
                ILGenerator ilGen = dm.GetILGenerator();

                ilGen.Emit(OpCodes.Ldarg_0);
                ilGen.Emit(OpCodes.Call, obj_parse);
                ilGen.Emit(OpCodes.Box, Tjson);
                ilGen.Emit(OpCodes.Ret);

                Call<object, Stream> helper =
                    dm.CreateDelegate(typeof(Call<object, Stream>)) as Call<object, Stream>;
                ParseCache.Add(Tjson, helper);

                return helper;
            }
        }

        internal static readonly Dictionary<Type, MethodInfo> M = new Dictionary<Type, MethodInfo>();
        static void Init_M() {
            M.Add(typeof(bool), new Exec<bool, StreamWriter>((bool V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V.ToString().ToLower());
            }).Method);
            M.Add(typeof(sbyte), new Exec<sbyte, StreamWriter>((sbyte V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(byte), new Exec<byte, StreamWriter>((byte V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(char), new Exec<char, StreamWriter>((char V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(short), new Exec<short, StreamWriter>((short V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(ushort), new Exec<ushort, StreamWriter>((ushort V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(int), new Exec<int, StreamWriter>((int V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(uint), new Exec<uint, StreamWriter>((uint V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(long), new Exec<long, StreamWriter>((long V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(ulong), new Exec<ulong, StreamWriter>((ulong V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(float), new Exec<float, StreamWriter>((float V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(double), new Exec<double, StreamWriter>((double V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(decimal), new Exec<decimal, StreamWriter>((decimal V, StreamWriter jsonWriter) => {
                jsonWriter.Write(V);
            }).Method);
            M.Add(typeof(string), new Exec<string, StreamWriter>((string V, StreamWriter jsonWriter) => {
                if(V == null) {
                    jsonWriter.Write(JsonHelper.NULL);
                    return;
                }

                jsonWriter.Write("\"");
                for(int i = 0, len = V.Length; i < len; i++) {
                    char c = V[i];
                    switch(c) {
                        case '\"':
                            jsonWriter.Write("\\\"");
                            continue;
                        case '\\':
                            jsonWriter.Write("\\\\");
                            continue;
                        case '/':
                            jsonWriter.Write("\\/");
                            continue;
                        case '\b':
                            jsonWriter.Write("\\b");
                            continue;
                        case '\f':
                            jsonWriter.Write("\\f");
                            continue;
                        case '\n':
                            jsonWriter.Write("\\n");
                            continue;
                        case '\r':
                            jsonWriter.Write("\\r");
                            continue;
                        case '\t':
                            jsonWriter.Write("\\t");
                            continue;
                        default: // \u
                            jsonWriter.Write(c);
                            continue;
                    }
                }
                jsonWriter.Write("\"");
                
            }).Method);
            M.Add(typeof(DateTime), new Exec<DateTime, StreamWriter>((DateTime V, StreamWriter jsonWriter) => {
                jsonWriter.Write("\"");
                jsonWriter.Write(V.ToString("r"));
                jsonWriter.Write("\"");
            }).Method);
            M.Add(typeof(byte[]), new Exec<byte[], StreamWriter>((byte[] V, StreamWriter jsonWriter) => {
                if(V == null) {
                    jsonWriter.Write(JsonHelper.NULL);
                    return;
                }

                jsonWriter.Write("\"");
                jsonWriter.Write(Convert.ToBase64String(V));
                jsonWriter.Write("\"");
            }).Method);
        }
    }
}
